﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace XDL.Framework.Particles
{
    /// <summary>
    /// A basic particle emitter that spawn particles around a half sphere.
    /// </summary>
    public class BlastParticleEmitter
    {
        ParticleSystem m_particleSystem;
        int m_min, m_max;
        float m_minVelocity, m_velocityRange;
        float m_minRadius, m_radiusRange;
        Random m_rnd = new Random();
        static Vector3[] g_randomUnitVectors = new Vector3[256];
        
        static BlastParticleEmitter()
        {
            /*
             * 
             * U(cos(p), 0, sin(p))
             * par rotation autour de Z (azimuth)
             * { 
             * cos(a), -sin(a), 0
             * sin(a), cos(a), 0,
             * 0 ,0 ,1
             * }
             * =
             * cos(p) * cos(a)
             * cos(p) sin(a)
             * sin(p)
             * 
             */

            Random rnd = new Random();
            for (int i = 0; i < g_randomUnitVectors.Length; i++)
            {
                // whole orizon
                double azimuthAngle = 2 * Math.PI * rnd.NextDouble();
                // only half-sphere
                double polarAngle = Math.PI * rnd.NextDouble();
                g_randomUnitVectors[i] = new Vector3(
                    (float)(Math.Cos(polarAngle) * Math.Cos(azimuthAngle)),
                    (float)(Math.Cos(polarAngle) * Math.Sin(azimuthAngle)),
                    (float)(Math.Sin(polarAngle)));
            }            
        }

        public BlastParticleEmitter(
            ParticleSystem ps,
            int minParticles,
            int maxParticles,
            float minRadius, 
            float maxRadius,
            float minVelocity,
            float maxVelocity)
        {
            if ( maxParticles < 0 ||
                 minParticles > maxParticles )
                throw new ArgumentOutOfRangeException("Invalid min/max particle count parameters");
            if (minVelocity > maxVelocity)
                throw new ArgumentOutOfRangeException("Invalid min/max particle velocity parameters");
            if (minRadius > maxRadius)
                throw new ArgumentOutOfRangeException("Invalid min/max radius parameters");

            m_particleSystem = ps;
            m_min = minParticles;
            m_max = maxParticles;
            m_minVelocity = minVelocity;
            m_velocityRange = maxVelocity - minVelocity;
            m_minRadius = minRadius;
            m_radiusRange = maxRadius - minRadius;
        }

        public void AddSpikes(Vector3 position, Vector3 velocity)
        {
            int n = m_rnd.Next(m_min, m_max);
            for (int i = 0; i < n; ++i)
            {
                int vectorID = m_rnd.Next(0, g_randomUnitVectors.Length);
                Vector3 randomUnitVector = g_randomUnitVectors[vectorID];
                Vector3 particleVelocity =
                    (float)(m_minVelocity + m_velocityRange * m_rnd.NextDouble()) * randomUnitVector +
                     m_particleSystem.Settings.EmitterVelocitySensitivity * velocity; 
                // create a number of "aged" particle along the random unit vector
                float radius = m_minRadius + m_radiusRange * (float)m_rnd.NextDouble();
                // create 20 particles (test)
                float dk = 0;
                for (int k = 0; k < 20; k++)
                {
                    m_particleSystem.AddParticle(
                        Vector3.Lerp(position,position + radius * randomUnitVector,dk),
                        particleVelocity,
                        1.0f - dk);
                    // next particle
                    dk += 1.0f / 20.0f;
                }
            }
        }

        public void AddSpikes(Matrix transform, Vector3 velocity)
        {
            int n = m_rnd.Next(m_min, m_max);
            for (int i = 0; i < n; ++i)
            {
                int vectorID = m_rnd.Next(0, g_randomUnitVectors.Length);
                // Vector3 randomUnitVector = Vector3.TransformNormal(g_randomUnitVectors[vectorID],transform);
                Vector3 randomUnitVector = Vector3.TransformNormal(new Vector3(0, 0, 1),transform);

                Vector3 particleVelocity =
                    (float)(m_minVelocity + m_velocityRange * m_rnd.NextDouble()) * randomUnitVector +
                    m_particleSystem.Settings.EmitterVelocitySensitivity * velocity;
                // create a number of "aged" particle along the random unit vector
                float radius = m_minRadius + m_radiusRange * (float)m_rnd.NextDouble();
                // create 10 particles (test)
                float dk = 0;
                for (int k = 0; k < 20; k++)
                {
                    m_particleSystem.AddParticle(
                        Vector3.Lerp(
                            transform.Translation, 
                            transform.Translation + radius * randomUnitVector, dk),
                        particleVelocity,
                        1.0f - dk);
                    // next particle
                    dk += 1.0f / 20.0f;
                }
            }
        }

        public void AddParticles(Vector3 position, Vector3 velocity)
        {
            int n = m_rnd.Next(m_min, m_max);
            for (int i = 0; i < n; ++i)
            {
                int vectorID = m_rnd.Next(0, g_randomUnitVectors.Length);
                Vector3 randomUnitVector = g_randomUnitVectors[vectorID];
                Vector3 particleVelocity =
                    (float)(m_minVelocity + m_velocityRange * m_rnd.NextDouble()) * g_randomUnitVectors[vectorID];
                m_particleSystem.AddParticle(
                    position + (float)(m_minRadius + m_radiusRange * m_rnd.NextDouble()) * randomUnitVector,
                    particleVelocity + velocity);
            }
        }

        public void AddParticles(Matrix transform, Vector3 velocity)
        {
            int n = m_rnd.Next(m_min, m_max);
            Vector3 position = transform.Translation;
            for (int i = 0; i < n; ++i)
            { 
                int vectorID = m_rnd.Next(0,g_randomUnitVectors.Length);
                Vector3 randomUnitVector = g_randomUnitVectors[vectorID];
                Vector3 particleVelocity =
                    (float)(m_minVelocity + m_velocityRange * m_rnd.NextDouble()) * g_randomUnitVectors[vectorID];
                m_particleSystem.AddParticle(
                    position + (float)(m_minRadius + m_radiusRange * m_rnd.NextDouble()) * randomUnitVector,
                    Vector3.TransformNormal(particleVelocity, transform) + velocity);
            }
        }
    }
}
